package com.walle.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Vector;

/**
 * @author zhangjiapeng
 * @Package com.walle.nio
 * @Description: ${todo}
 * @date 2019/1/17 11:48
 */
public class ChatServer implements Runnable {


    public Selector selector;

    private SelectionKey serverKey;

    private Vector<String> usernames;

    private static final int PORT = 9999;

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");


    public ChatServer() {
        usernames = new Vector<>();
        init();
    }

    public void init(){
        try {
            selector = Selector.open();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            ServerSocket serverSocket = serverSocketChannel.socket();
            serverSocket.bind(new InetSocketAddress(PORT));
            serverSocketChannel.configureBlocking(false);
            serverKey = serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
            System.out.println("server starting.......");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            while (true){
                int count = selector.select();
                if(count > 0){
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while(iterator.hasNext()){
                        SelectionKey key = iterator.next();
                        if(key.isAcceptable()){
                            System.out.println(key.toString() + ":接收");
                            iterator.remove();
                            ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                            SocketChannel socketChannel = serverSocketChannel.accept();
                            socketChannel.configureBlocking(false);
                            socketChannel.register(selector,SelectionKey.OP_READ);
                        }

                        if(key.isValid() && key.isReadable()){
                            System.out.println(key.toString() + ":读");
                            readMsg(key);
                        }

                        if(key.isValid() && key.isWritable()){
                            System.out.println(key.toString() + ":写");
                            writeMsg(key);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void readMsg(SelectionKey key){
        SocketChannel socketChannel = null;
        try {
            socketChannel = (SocketChannel) key.channel();
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
            int count = socketChannel.read(readBuffer);
            StringBuilder msgBuilder = new StringBuilder();
            while(count > 0){
                readBuffer.flip();
                msgBuilder.append(new String(readBuffer.array(), 0, count));
                readBuffer.clear();
                count = socketChannel.read(readBuffer);
            }
            String msg = msgBuilder.toString();
            System.out.println(msg);
            if(msg.indexOf("open_") != -1){
                String name = msg.substring(5);
                System.out.println(name + "->上线");
                usernames.add(name);
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while(iterator.hasNext()){
                    SelectionKey skey = iterator.next();
                    if(skey != key){
                        skey.attach(name);
                    }
                    skey.interestOps(skey.interestOps() | SelectionKey.OP_WRITE);
                }
            }else if(msg.indexOf("exit_") != -1){
                String name = msg.substring(5);
                System.out.println(name + "->下线");
                key.attach("close");
                usernames.remove(name);
                key.interestOps(SelectionKey.OP_WRITE);
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while(iterator.hasNext()){
                    SelectionKey skey = iterator.next();
                    skey.attach(name);
                    skey.interestOps(skey.interestOps() | SelectionKey.OP_WRITE);
                }
            }else{
                String uname = msg.substring(0, msg.indexOf("^"));
                msg = msg.substring(msg.indexOf("^") + 1);
                System.out.println("("+uname+")说：" + msg);
                String dateTime = sdf.format(new Date());
                String smsg = uname + " " + dateTime + "\n  " + msg + "\n";
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while(iterator.hasNext()){
                    SelectionKey skey = iterator.next();
                    skey.attach(smsg);
                    skey.interestOps(skey.interestOps() | SelectionKey.OP_WRITE);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            key.cancel();
            try {
                socketChannel.socket().close();
                socketChannel.close();
            } catch (IOException e2) {
                e2.printStackTrace();
            }
        }
    }


    public void writeMsg(SelectionKey key){
        try {
            SocketChannel channel = (SocketChannel) key.channel();
            Object attachment = key.attachment();
            //获取key的值之后，要把key的值置空，避免影响下一次的使用
            key.attach("");
            if(attachment != null){
                channel.write(ByteBuffer.wrap(attachment.toString().getBytes()));
            }

            key.interestOps(key.interestOps() | SelectionKey.OP_READ);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ChatServer server = new ChatServer();
        new Thread(server).start();
    }
}
